
namespace Patterns4Net.Core.AttributesPatterns
{
    using System.Diagnostics.Contracts;
    using System.Linq;
    using Mono.Cecil;
    using Attributes.DesignPatterns;
    using CecilExtensions;
    using Patterns;
    using Attributes;

    /// <summary>
    /// Strategy class for creating the <see cref="Composite"/> class from the <see cref="CompositeAttribute"/> meta-data.
    /// </summary>
    [PatternCreatorExport(typeof(CompositeAttribute))]
    public class CompositeCreator : ICreatePatternFromAttribute
    {
        private const string ComponentResolveMessage =
            @"Cannot resolve component type in Composite pattern implemented by {0}.";

        public IPattern Crate(CustomAttribute attribute, IMemberDefinition targetType)
        {
            Contract.Assume(targetType is TypeDefinition);

            if (attribute.ConstructorArguments.Count == 0)
            {
                if (attribute.Fields.Count == 1)
                {
                    Contract.Assume(attribute.Fields.First().Name == "Component");
                    Contract.Assume(attribute.Fields.First().Argument.Value is TypeReference);
                    var component = ((TypeReference)attribute.Fields.First().Argument.Value)
                        .SafeResolve(GetComponentResolveMessage(targetType));
                    return new Composite((TypeDefinition)targetType, component);
                }

                return new Composite((TypeDefinition)targetType, CreateComponentFromTargetType((TypeDefinition)targetType));
            }

            Contract.Assume(attribute.ConstructorArguments.Count == 1);
            var arg = attribute.ConstructorArguments.First();
            Contract.Assume(arg.Value is TypeReference);
            return new Composite((TypeDefinition)targetType, ((TypeReference)arg.Value).SafeResolve(GetComponentResolveMessage(targetType)));
        }

        private static TypeDefinition CreateComponentFromTargetType(TypeDefinition targetType)
        {
            var type = targetType.SafeResolve("Cannot resolve type that implements Composite pattern.");
            if (type.BaseType.IsEqual(typeof(object)) == false)
            {
                return type.BaseType.SafeResolve();
            }

            if (type.Interfaces.Count == 1)
            {
                return type.Interfaces.First().SafeResolve(GetComponentResolveMessage(targetType));
            }

            return type;
        }

        private static string GetComponentResolveMessage(IMemberDefinition target)
        {
            return string.Format(ComponentResolveMessage, target.Name);
        }
    }
}
